package com.fmyexample.v2mcsecuritybootjdbc.core;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;


/**
 * @author Mr.fmy
 * @version 1.0.0
 * @ClassName ToolUtils
 * @Description TODO 常用 工具集
 * @createTime 2019/10/11 14:44
 */
public class ToolKit {

    /**
     * 普通 MD5 拼接 盐值 加密
     */
    public static String md5Salt(String salt, String pass) {
        return SecureUtil.md5(salt + pass);
    }

    /**
     * 1-7 对应 周一-周日
     */
    //public static String week(Integer value) {
    //    BizExceptionKit.throwBusiness(value < 1 || value > 7, "value 不符合要求");
    //    value = value == 7 ? 1 : value + 1;
    //    return Week.of(value).toChinese("周");
    //}

    //public static void mapNewValue(Map<String, Object> map, String key, BaseIEnum[] enums) {
    //    mapNewValue(map, key, item -> BaseIEnum.valueOf(item, enums));
    //}

    //public static <T extends BaseIEnum<R>, R extends Serializable> void mapNewEnumToNewKey(Map<String, Object> map,
    //                                                                                       String key, Class<T> enums) {
    //    mapNewValue(map, key, item -> {
    //        if (ObjectUtil.isNotEmpty(item)) {
    //            T t = BaseIEnum.getValueOf((R) item, enums);
    //            if (t == null) {
    //                return item;
    //            }
    //            map.put(key + "Cn", t.getDesc());
    //            return ((Enum) t).name();
    //        } else {
    //            return null;
    //        }
    //    });
    //}

    //public static <T extends BaseIEnum<R>, R extends Serializable> void mapNewValueEnumName(Map<String, Object> map,
    //                                                                                        String key, Class<T>
    //                                                                                                enums) {
    //    mapNewValue(map, key, item -> {
    //        if (ObjectUtil.isEmpty(item)) {
    //            return null;
    //        }
    //        return BaseIEnum.getValueOfName((R) item, enums);
    //    });
    //}

    //public static void mapNewValue(Map<String, Object> map, String key, BaseIEnum[]
    //        enums, String defult) {
    //    mapNewValue(map, key, item -> {
    //        if (ObjectUtil.isNotEmpty(item)) {
    //            String value = BaseIEnum.valueOf(item, enums);
    //            return StrUtil.isBlank(value) ? defult : value;
    //        } else {
    //            return null;
    //        }
    //    });
    //}

    public static void mapNewKey(Map<String, Object> map, String key, Function<Object, Object> function) {
        if (map.containsKey(key)) {
            map.put(key + "Cn", function.apply(map.get(key)));
        }
    }

    /**
     * map 同一 key 覆盖新的 value
     * <p>
     * 如果 key不存在、不进行处理
     * </p>
     *
     * @param function 入参 本来的value 返回 新的value
     */
    public static void mapNewValue(Map<String, Object> map, String key, Function<Object, Object> function) {
        if (map.containsKey(key)) {
            map.put(key, function.apply(map.get(key)));
        }
    }

    /**
     * endTime - startTime
     */
    public static Duration between(LocalDateTime startTime, LocalDateTime endTime) {
        return Duration.between(startTime, endTime);
    }

    public static LocalDateTime toLocalDateTime(Object value) {
        if (Objects.isNull(value)) return null;
        if (value instanceof Timestamp) return ((Timestamp) value).toLocalDateTime();
        if (value instanceof Date) return ((Date) value).toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();
        return null;
    }

    /**
     * 经纬度 定点 距离计算
     *
     * @param longitude 经度
     * @param latitude  维度
     * @param distance  距离  单位千米
     * @return 0 最小经度 1 最大经度 2 最小维度 3 最大维度
     */
    public static double[] neighPosition(double longitude, double latitude, double distance) {
        double r = 6371;//地球半径 千米
        double dlng = 2 * Math.asin(Math.sin(distance / (2 * r)) / Math.cos(latitude * Math.PI / 180));
        dlng = NumberUtil.div(NumberUtil.mul(dlng, 180), Math.PI);//角度转为弧度
        double dlat = NumberUtil.div(distance, r);
        dlat = NumberUtil.div(NumberUtil.mul(dlat, 180), Math.PI);
        double minlat = NumberUtil.sub(latitude, dlat);
        double maxlat = NumberUtil.add(latitude, dlat);
        double minlng = NumberUtil.sub(longitude, dlng);
        double maxlng = NumberUtil.add(longitude, dlng);
        return new double[]{NumberUtil.round(minlng, 6, RoundingMode.DOWN).doubleValue(),
                NumberUtil.round(maxlng, 6, RoundingMode.DOWN).doubleValue(),
                NumberUtil.round(minlat, 6, RoundingMode.DOWN).doubleValue(),
                NumberUtil.round(maxlat, 6, RoundingMode.DOWN).doubleValue()};
    }

    /**
     * 根据经纬度、获取两点相聚距离
     *
     * @param lng1 开始经度
     * @param lat1 开始维度
     * @param lng2 对比经度
     * @param lat2 对比维度
     * @return 单位 千米
     */
    public static String getDistance(double lng1, double lat1, double lng2, double lat2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = NumberUtil.mul(s, 6371);
        s = NumberUtil.div(Math.round(s * 10000), 10000);
        return NumberUtil.decimalFormat("0.00", s);
    }


    public static String distance(double lat1, double lng1, double lat2, double lng2) {
        double x1 = Math.cos(lat1) * Math.cos(lng1);
        double y1 = Math.cos(lat1) * Math.sin(lng1);
        double z1 = Math.sin(lat1);

        double x2 = Math.cos(lat2) * Math.cos(lng2);
        double y2 = Math.cos(lat2) * Math.sin(lng2);
        double z2 = Math.sin(lat2);

        double lineDistance =
                Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2));
        double realDistance = 6378.137f * Math.PI * 2 * Math.asin(0.5 * lineDistance) / 180;
        return NumberUtil.decimalFormat("0.00", realDistance);
    }

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    public static BigDecimal toBigDecimal(Object value, BigDecimal defult) {
        if (ObjectUtil.isEmpty(value)) {
            return defult;
        }
        return NumberUtil.toBigDecimal(value.toString());
    }

    public static Integer toInt(Object val, int defaultVal) {
        if (ObjectUtil.isEmpty(val)) {
            return defaultVal;
        }
        if (val instanceof Double) {
            BigDecimal bigDecimal = new BigDecimal((Double) val);
            return bigDecimal.intValue();
        } else {
            return NumberUtil.parseInt(val.toString());
        }
    }
}
